<template>
  <div class="sau" :class="{ 'is-active': sauInstance && sauInstance?.status }" :style="sauStyle">
    <el-icon class="close-icon" :size="18" :color="'#FF7071'" @click="deleteNode">
      <CircleCloseFilled />
    </el-icon>
    <Handle id="sau_a" type="target" :position="Position.Left" />

    <div class="ps-header">
      <div class="title">
        <span style="margin-right: 10px">SAU</span>
        <el-tag v-if="sauInstance?.status" type="success" effect="dark">{{
          sauInstance?.status
        }}</el-tag>
      </div>
      <div class="refresh-btn">
        <el-tooltip effect="dark" content="点击复制" placement="top">
          <span class="sau-id" @click="copyText(sauInstance?.sauInstanceId)">{{
            sauInstance?.sauInstanceId
          }}</span>
        </el-tooltip>
        <el-tooltip effect="dark" content="刷新" placement="top">
          <el-icon @click="refreshSauInstance(node)" :size="18" color="#00b23c"
            ><Refresh
          /></el-icon>
        </el-tooltip>
      </div>
      <!-- <div class="expand-btn" @click="isExpand = !isExpand">{{ isExpand ? '收起' : '展开' }}</div> -->
    </div>
    <div class="ps-body">
      <el-form ref="sauFormRef" :model="sauData" :rules="rules" label-width="110px">
        <el-form-item label="计算节点" prop="computeNodeId">
          <el-select
            v-model="sauData.computeNodeId"
            clearable
            style="width: 100%"
            placeholder="请选择计算节点"
            @change="handleUpdateNode"
            :disabled="!!sauInstance"
          >
            <el-option
              v-for="item in computeNodeList"
              :key="item.computeNodeId"
              :label="item.computeName + '(' + item.status + ')'"
              :value="item.computeNodeId"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="模板选择" prop="sauTemplateId">
          <el-select
            v-model="sauData.sauTemplateId"
            clearable
            style="width: 100%"
            placeholder="请选择模板"
            @change="templateChange"
            :disabled="!!sauInstance"
          >
            <el-option
              v-for="item in sauTemplateList"
              :key="item.sauTemplateId"
              :label="item.sauTemplateName"
              :value="item.sauTemplateId"
            />
          </el-select>
          <div class="input-type-result">
            {{ templateDesc }}
          </div>
        </el-form-item>

        <el-form-item label="输入元信息类型" v-if="sauData.sauTemplateId">
          {{
            sauTemplateList.find((item) => item.sauTemplateId === sauData.sauTemplateId)
              ?.inputMetaType
          }}
        </el-form-item>

        <el-form-item label="输入信号类型" v-if="sauData.sauTemplateId">
          {{ getTemplateInputSignalType(sauData.sauTemplateId) }}
        </el-form-item>
        <template v-if="Object.keys(connectedSensorRecord).length !== 0">
          <el-form-item label="输入信号列表" style="margin-bottom: 0px" />

          <SensorSort
            v-model="connectedSensorRecord"
            :disabled="!!sauInstance"
            @update="handleUpdateNode"
          />
        </template>
      </el-form>

      <div class="sau-operate">
        <el-button type="primary" @click="createSau" v-if="!sauInstance?.sauInstanceId"
          >创建实例</el-button
        >
        <el-button v-else type="default" @click="deleteSauInstance">删除实例</el-button>
      </div>
    </div>

    <Handle id="sau_b" type="source" :position="Position.Right" />
  </div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { CircleCloseFilled, Close, Refresh } from '@element-plus/icons-vue'
import { Handle, Position, useVueFlow } from '@vue-flow/core'
import { FormInstance, FormRules } from 'element-plus'
import SensorSort from './SensorSort.vue'
import { AlgengineApi } from '@/api/vmiot/algengine'

const props = defineProps<{
  node: any
  taskHook: any
}>()

const emit = defineEmits(['sauChange'])

const {
  updateNode,
  getConnectedEdges,
  removeNodes,
  toObject,
  removeEdges,
  getSelectedEdges,
  removeSelectedEdges
} = useVueFlow()
const {
  isInit,
  sauTemplateList,
  computeNodeList,
  sauInstanceMap,
  getTemplateInputSignalType,
  generateSensorColumnRecord,
  transposeLists,
  hasEmptySensor,
  setSensorColumnRecordData,
  isMatchSignalType,
  addNewSensor,
  refreshSauInstance
} = props.taskHook
const { node } = toRefs(props)
const sensorSortVisible = ref(false)

const sauFormRef = ref<FormInstance>()
const sauData = ref({
  sauTemplateId: '',
  computeNodeId: ''
})

const isExpand = ref(true)

if (node.value.data.sauData) {
  sauData.value = node.value.data.sauData
}

const connectedSensorRecord = ref<any>([])

const sauInstance = computed(() => {
  const entries = Array.from(sauInstanceMap.value.entries())
  const entry = entries.find(([key, _]) => key === node.value.data.sauInstanceId)
  const result = entry ? entry[1] : undefined
  return result
})

const rules = reactive<FormRules>({
  sauTemplateId: [{ required: true, message: '请选择模板', trigger: 'change' }],
  computeNodeId: [{ required: true, message: '请选择计算节点', trigger: 'change' }]
})

const handleUpdateNode = () => {
  updateNode(node.value.id, {
    data: {
      sauData: sauData.value,
      sauInstanceId: sauInstance.value?.sauInstanceId || ''
    }
  })
}

/**
 * sau模板改变
 */
const templateChange = () => {
  calcConnectedSensorList()
  handleUpdateNode()
}

/**
 * 计算跟sau连接的物理传感器列表，类型不匹配删除连线
 */
const calcConnectedSensorList = () => {
  // 生成传感器排列数据，空数据格式
  connectedSensorRecord.value = generateSensorColumnRecord(sauData.value.sauTemplateId)
  // 模版改变，去除类型不匹配的连线
  const { nodes } = { ...toObject() }
  const connectedEdges = getConnectedEdges(node.value.id)
  connectedEdges.forEach((item) => {
    if (item.sourceHandle === 'sensor_a' && item.targetHandle === 'sau_a') {
      const sourceNode = nodes.find((node) => node.id === item.source)
      if (
        sourceNode &&
        isMatchSignalType(sauData.value.sauTemplateId, sourceNode.data.sensorData.sensorId)
      ) {
        addNewSensor(connectedSensorRecord.value, sourceNode.data.sensorData)
      } else {
        removeEdges([item])
      }
    }
  })
}

/**
 * 确认删除sau节点
 */
const deleteNode = () => {
  removeSelectedEdges(getSelectedEdges.value)
  removeNodes([node.value.id])
}

/**
 * 显示排序
 */
const showSensorSort = async () => {
  if (!sauFormRef.value) return
  await sauFormRef.value.validate((valid) => {
    if (valid) {
      sensorSortVisible.value = true
    }
  })
}

/**
 *
 * @param physicalSensorArray 物理传感器数据列表
 * @param sauTemplateId 模版id
 */
const getInputMetaList = (physicalSensorArray: string[][], sauTemplateId: string) => {
  const inputMetaTypeFromTemplate = sauTemplateList.value.find(
    (item) => item.sauTemplateId === sauTemplateId
  )?.inputMetaType

  const inputMetaList: any = []
  const { nodes } = { ...toObject() }

  for (let rowIndex = 0; rowIndex < physicalSensorArray.length; rowIndex++) {
    const row = physicalSensorArray[rowIndex]

    for (const item of row) {
      //新取数据，防止有误
      const realSensor = nodes.find(
        (nodeItem) => nodeItem.data.sensorData?.sensorObj.sensorId === item
      )?.data.sensorData

      for (const meta of inputMetaTypeFromTemplate) {
        let metaValue = ''

        if (meta === 'SPECS') {
          metaValue = realSensor.sensorObj?.sensorSpecs
        } else if (meta === 'POS') {
          if (!realSensor.positionX || !realSensor.positionY || !realSensor.positionZ) {
            ElMessage.error(`物理传感器 (${realSensor.sensorId}) 的空间位置信息不能为空`)
            return false
          }

          const tempMetaObj = {
            coordinate: {
              x: realSensor.positionX,
              y: realSensor.positionY,
              z: realSensor.positionZ
            },
            relativeOrigin: rowIndex + 1 // 这里是关键，行索引从1开始
          }

          metaValue = JSON.stringify(tempMetaObj)
        }

        inputMetaList.push({
          physicalSensorId: item,
          inputMetaType: meta,
          meta: metaValue
        })
      }
    }
  }

  return inputMetaList
}

/**
 * 创建sau实例，校验表单数据
 */
const createSau = async () => {
  if (!sauFormRef.value) return
  await sauFormRef.value.validate((valid) => {
    if (valid) {
      createSauInstance()
    }
  })
}

/**
 * 物理传感器ID去重验证
 * 1、验证每一行数据不能有重复id
 * 2、验证行与行数据不能有重复，无关顺序
 * @param deviceArray
 */
const validateDeviceArray = (deviceArray: string[][]): true | string => {
  const rowSetList: Set<string>[] = []

  for (let i = 0; i < deviceArray.length; i++) {
    const row = deviceArray[i]
    const set = new Set(row)

    // 行内重复校验
    if (set.size !== row.length) {
      return `第 ${i + 1} 行设备ID重复`
    }

    // 跨行重复（无序比较）
    for (let j = 0; j < i; j++) {
      if (isSetEqual(set, rowSetList[j])) {
        return `第 ${i + 1} 行与第 ${j + 1} 行设备ID重复`
      }
    }

    rowSetList.push(set)
  }

  return true
}

const isSetEqual = (setA: Set<string>, setB: Set<string>): boolean => {
  if (setA.size !== setB.size) return false
  for (const item of setA) {
    if (!setB.has(item)) return false
  }
  return true
}

/**
 * 创建sau实例
 */
const createSauInstance = async () => {
  const physicalSensorList = transposeLists(connectedSensorRecord.value)

  if (physicalSensorList.length === 0) {
    ElMessage.error('请选择物理传感器并连接至SAU')
    return
  }

  if (hasEmptySensor(physicalSensorList)) {
    ElMessage.error('模板中存在未选择的物理传感器，请继续选择或重新排列传感器')
    return
  }

  const lastInputMetaList = getInputMetaList(physicalSensorList, sauData.value.sauTemplateId)

  if (!lastInputMetaList) {
    return
  }

  const result = validateDeviceArray(physicalSensorList)
  if (result !== true) {
    ElMessage.error(result)
    return
  }

  const params = {
    operateType: 0,
    computeNodeId: sauData.value.computeNodeId,
    sauTemplateId: sauData.value.sauTemplateId,
    physicalSensorList: physicalSensorList,
    inputMetaList: lastInputMetaList
  }

  const res = await AlgengineApi.createSau(params)
  ElMessage.success('创建SAU实例成功')
  updateNode(node.value.id, {
    data: {
      sauData: sauData.value,
      sauInstanceId: res.sauInstanceId
    }
  })
  const newSauInstance = await AlgengineApi.getSauDetail({
    sauInstanceId: res.sauInstanceId
  })
  sauInstanceMap.value.set(res.sauInstanceId, newSauInstance)
  setTimeout(() => {
    emit('sauChange')
  })
}

/**
 * 删除sau实例
 */
const deleteSauInstance = () => {
  ElMessageBox.confirm('确认删除SAU实例吗？', '提示', {
    confirmButtonText: '确 认',
    cancelButtonText: '取 消',
    type: 'warning'
  })
    .then(async () => {
      await AlgengineApi.deleteSau(sauInstance.value.sauInstanceId)
      ElMessage.success('删除SAU实例成功')
      sauInstanceMap.value.delete(sauInstance.value.sauInstanceId)
      updateNode(node.value.id, {
        data: {
          sauData: sauData.value,
          sauInstanceId: ''
        }
      })

      setTimeout(() => {
        emit('sauChange')
      })
    })
    .catch(() => console.info('操作取消'))
}

const templateDesc = computed(() => {
  const template = sauTemplateList.value.find(
    (item) => item.sauTemplateId === sauData.value.sauTemplateId
  )
  return template?.description || ''
})

function copyText(content) {
  if (navigator.clipboard) {
    navigator.clipboard
      .writeText(content)
      .then(() => {
        ElMessage.success('复制成功')
      })
      .catch((err) => {
        console.error(err)
        ElMessage.success('复制失败')
      })
  } else {
    // 兼容旧浏览器
    legacyCopy(content)
  }
}

function legacyCopy(content) {
  const textarea = document.createElement('textarea')
  textarea.value = content
  textarea.style.position = 'fixed'
  textarea.style.opacity = '0'
  document.body.appendChild(textarea)
  textarea.select()
  try {
    document.execCommand('copy')
    ElMessage.success('复制成功')
  } catch (err) {
    console.error(err)
    ElMessage.success('复制失败')
  }
  document.body.removeChild(textarea)
}

const sauStyle = computed(() => {
  let w
  if (sauData.value.sauTemplateId) {
    const outLen = getTemplateInputSignalType(sauData.value.sauTemplateId).length
    w = 200 + outLen * 80
  } else {
    w = 400
  }
  return {
    width: `${w}px`
  }
})

watch(
  () => isInit,
  (newVal) => {
    if (newVal) {
      connectedSensorRecord.value = generateSensorColumnRecord(sauData.value.sauTemplateId)

      if (node.value.data.sauInstanceId && sauInstance.value) {
        setSensorColumnRecordData(
          connectedSensorRecord.value,
          sauInstance.value?.physicalSensorList
        )
      } else {
        calcConnectedSensorList()
      }
    }
  },
  {
    immediate: true
  }
)

defineExpose({
  calcConnectedSensorList
})
</script>
<style lang="scss" scoped>
.sau {
  min-width: 400px;
  height: auto;
  border-radius: 4px;
  padding: 10px;

  &.is-active {
    background: linear-gradient(180deg, #e8edff 0%, #ffffff 20%, #ffffff 100%);
  }

  cursor: pointer;
  .close-icon {
    position: absolute;
    top: -15px;
    right: -15px;
    font-size: 18px;
  }

  .ps-header {
    background: url('@/assets/task/task02.png') 12px center / 25px 25px no-repeat;
    padding: 4px 10px 4px 49px;
    color: #333;
    font-size: 14px;
    display: flex;
    justify-content: space-between;

    .title {
      display: flex;
      align-items: center;
    }

    .refresh-btn {
      display: flex;
      align-items: center;
      .sau-id {
        margin: 0 15px;
      }
    }

    .expand-btn {
      color: #5674e6;
    }
  }

  .ps-body {
    padding: 10px 15px 0;
  }
}

.input-type-result {
  color: #888;
  font-size: 12px;
  line-height: 1.5;
  text-align: left;
}

.sensor-sort-content {
  .close-icon {
    position: absolute;
    top: 10px;
    right: 10px;
    font-size: 18px;
    color: #999;
    cursor: pointer;
  }
}

.sau-operate {
  margin-top: 10px;
  .el-button--primary {
    background: linear-gradient(90deg, #1a40cb 0%, #5775e7 100%);
    border: 0;
    color: #fff !important;
  }

  .el-button--default {
    border: 1px solid #5473e6;
    color: #5271e5;
  }
}

.test {
  width: 300px;
  overflow: auto;
  border: 1px solid #999;
  position: relative;

  .test-c {
    height: 50px;
  }

  .grid-h {
    display: grid;
    grid-auto-rows: 40px;
    gap: 10px;
    grid-template-columns: repeat(100, minmax(100px, 1fr));
    min-width: 100%;
  }

  .grid-col {
    height: 50px;
    background-color: #1a40cb;
    color: #fff;
  }
}
</style>
